;;; init-minibuffer.el --- Configuration for minibuffer -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; vertico
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun crm-indicator (args)
  (cons (format "[CRM%s] %s"
                (replace-regexp-in-string
                 "\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
                 crm-separator)
                (car args))
        (cdr args)))
(advice-add #'completing-read-multiple :filter-args #'crm-indicator)

(setq read-file-name-completion-ignore-case t
      read-buffer-completion-ignore-case t)

(add-hook 'after-init-hook 'vertico-mode)
(with-eval-after-load 'vertico
  (setq vertico-cycle t)

  (require 'vertico-directory)
  (define-key vertico-map (kbd "RET") #'vertico-directory-enter)
  (define-key vertico-map (kbd "DEL") #'vertico-directory-delete-char)
  (add-hook 'rfn-eshadow-update-overlay-hook #'vertico-directory-tidy))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; orderless
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 'orderless)


(defun +orderless--consult-suffix ()
  "Regexp which matches the end of string with Consult tofu support."
  (if (and (boundp 'consult--tofu-char) (boundp 'consult--tofu-range))
      (format "[%c-%c]*$"
              consult--tofu-char
              (+ consult--tofu-char consult--tofu-range -1))
    "$"))

;; Recognizes the following patterns:
;; * .ext (file extension)
;; * regexp$ (regexp matching at end)
(defun +orderless-consult-dispatch (word _index _total)
  (cond
   ;; Ensure that $ works with Consult commands, which add disambiguation suffixes
   ((string-suffix-p "$" word)
    `(orderless-regexp . ,(concat (substring word 0 -1) (+orderless--consult-suffix))))
   ;; File extensions
   ((and (or minibuffer-completing-file-name
             (derived-mode-p 'eshell-mode))
         (string-match-p "\\`\\.." word))
    `(orderless-regexp . ,(concat "\\." (substring word 1) (+orderless--consult-suffix))))))


(setq completion-styles '(orderless basic))

(setq completion-category-overrides '((file (styles orderless partial-completion))))

(setq orderless-matching-styles '(orderless-initialism
                                  orderless-literal
                                  orderless-regexp
                                  ))

(setq orderless-style-dispatchers (list #'+orderless-consult-dispatch
                                        #'orderless-affix-dispatch))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; consult
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun ctrlwei/consult-ripgrep-at-point (&optional dir initial)
  (interactive "P")
  (let ((s (symbol-at-point)))
    (if s (consult-ripgrep dir (symbol-name s))
      (consult-ripgrep dir initial))))

(global-set-key [remap switch-to-buffer] #'consult-buffer)
(global-set-key [remap switch-to-buffer-other-window] #'consult-buffer-other-window)
(global-set-key [remap goto-line] #'consult-goto-line)
(global-set-key [remap repeat-complex-command] #'consult-complex-command)
(global-set-key (kbd "C-c k") #'ctrlwei/consult-ripgrep-at-point)
(global-set-key (kbd "M-g i") #'consult-imenu)
(global-set-key (kbd "M-g I") #'consult-imenu-multi)

(with-eval-after-load 'consult
  (require 'embark-consult)

  (setq consult-narrow-key "<")

  ;; Do not show hidden buffers by default
  (setq consult-buffer-filter '("\\`[ *]" "magit[-:]"))

  ;; Use Orderless as pattern compiler for consult-grep/ripgrep/find
  (defun consult--orderless-regexp-compiler (input type &rest _config)
    (setq input (orderless-pattern-compiler input))
    (cons
     (mapcar (lambda (r) (consult--convert-regexp r type)) input)
     (lambda (str) (orderless--highlight input t str))))
  (defun ctrlwei-consult-toggle-regexp-compiler ()
    "Command to enable/disable preview."
    (interactive)
    (if (eq consult--regexp-compiler 'consult--default-regexp-compiler)
        (setq consult--regexp-compiler 'consult--orderless-regexp-compiler)
      (setq consult--regexp-compiler 'consult--default-regexp-compiler)))
  (define-key vertico-map (kbd "M-O") #'ctrlwei-consult-toggle-regexp-compiler)

  ;; Narrowing which-key help without delay
  (defun immediate-which-key-for-narrow (fun &rest args)
    (let* ((refresh t)
           (timer (and consult-narrow-key
                       (memq :narrow args)
                       (run-at-time 0.05 0.05
                                    (lambda ()
                                      (if (eq last-input-event (elt consult-narrow-key 0))
                                          (when refresh
                                            (setq refresh nil)
                                            (which-key--update))
                                        (setq refresh t)))))))
      (unwind-protect
          (apply fun args)
        (when timer
          (cancel-timer timer)))))
  (advice-add #'consult--read :around #'immediate-which-key-for-narrow)

  (consult-customize
   consult--source-hidden-buffer
   :state #'consult--buffer-state
   ctrlwei/consult-ripgrep-at-point
   consult-ripgrep consult-git-grep consult-grep
   consult-bookmark consult-recent-file consult-xref
   consult--source-recent-file consult--source-project-recent-file consult--source-bookmark
   :preview-key "M-.")


  ;; Toggle preview during active completion session
  (defvar-local consult-toggle-preview-orig nil)
  (defun consult-toggle-preview ()
    "Command to enable/disable preview."
    (interactive)
    (if consult-toggle-preview-orig
        (setq consult--preview-function consult-toggle-preview-orig
              consult-toggle-preview-orig nil)
      (setq consult-toggle-preview-orig consult--preview-function
            consult--preview-function #'ignore)))
  (define-key vertico-map (kbd "M-P") #'consult-toggle-preview)
  )

;; Key bindings for consult-dir
(global-set-key (kbd "C-x C-d") #'consult-dir)
(define-key minibuffer-mode-map (kbd "C-x C-d") #'consult-dir)
(define-key minibuffer-mode-map (kbd "C-x C-j") #'consult-dir-jump-file)

(with-eval-after-load 'xref
  (setq xref-history-storage #'xref-window-local-history)
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; embark
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(global-set-key (kbd "M-u") #'embark-act)
(global-set-key (kbd "C-h B") #'embark-bindings)
(with-eval-after-load 'embark
  (require 'embark-consult)

  ;; (define-key embark-general-map (kbd "<backspace>") #'delete-region)  ;; for intellij-backspace
  (define-key embark-general-map (kbd "W") #'kill-region)
  (define-key embark-file-map (kbd "e") #'eaf-open)
  (define-key embark-file-map (kbd "U") #'sudo-find-file)
  (define-key embark-symbol-map (kbd "h") #'helpful-symbol)
  (add-hook 'embark-collect-mode-hook 'consult-preview-at-point-mode)

  ;; {{ Use which-key like a key menu prompt.
  (defun embark-which-key-indicator ()
    "An embark indicator that displays keymaps using which-key.
The which-key help message will show the type and value of the
current target followed by an ellipsis if there are further
targets."
    (lambda (&optional keymap targets prefix)
      (if (null keymap)
          (which-key--hide-popup-ignore-command)
        (which-key--show-keymap
         (if (eq (plist-get (car targets) :type) 'embark-become)
             "Become"
           (format "Act on %s '%s'%s"
                   (plist-get (car targets) :type)
                   (embark--truncate-target (plist-get (car targets) :target))
                   (if (cdr targets) "…" "")))
         (if prefix
             (pcase (lookup-key keymap prefix 'accept-default)
               ((and (pred keymapp) km) km)
               (_ (key-binding prefix 'accept-default)))
           keymap)
         nil nil t (lambda (binding)
                     (not (string-suffix-p "-argument" (cdr binding))))))))

  (setq embark-indicators
        '(embark-which-key-indicator
          embark-highlight-indicator
          embark-isearch-highlight-indicator))

  (defun embark-hide-which-key-indicator (fn &rest args)
    "Hide the which-key indicator immediately when using the completing-read prompter."
    (which-key--hide-popup-ignore-command)
    (let ((embark-indicators
           (remq #'embark-which-key-indicator embark-indicators)))
      (apply fn args)))

  (advice-add #'embark-completing-read-prompter
              :around #'embark-hide-which-key-indicator)
  ;; }}

  ;; {{ Open any buffer by splitting any window.
  (defmacro ctrlwei/embark-ace-action (fn)
    `(defun ,(intern (concat "ctrlwei/embark-ace-" (symbol-name fn))) ()
       (interactive)
       (if (< 1 (length (window-list)))
           (progn
             (require 'ace-window)
             (let ((aw-dispatch-always t)
                   (message-log-max nil))
               (setq current-window-after-action (aw-select " [Embark] Ace - Window"))
               (if current-window-after-action
                   (aw-switch-to-window current-window-after-action))
               (call-interactively #',fn)
               (message " ")))
         (call-interactively #',(intern (concat (symbol-name fn) "-other-window"))))))

  (define-key embark-file-map (kbd "o") (ctrlwei/embark-ace-action find-file))
  (define-key embark-buffer-map (kbd "o") (ctrlwei/embark-ace-action switch-to-buffer))
  (define-key embark-bookmark-map (kbd "o") (ctrlwei/embark-ace-action bookmark-jump))
  ;; }}
  )

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; marginalia
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(add-hook 'vertico-mode-hook 'marginalia-mode)
(with-eval-after-load 'marginalia
  (setq marginalia-max-relative-age -1)
  (setq marginalia-align 'right)

  ;; {{ Change default date format in marginalia
  (defun ctrlwei/marginalia--time-absolute (time)
    (format-time-string "%y/%m/%d|%W-%w %H:%M" time))
  (advice-add #'marginalia--time-absolute :override #'ctrlwei/marginalia--time-absolute)
  ;; }}
  )

(provide 'init-minibuffer)
;;; init-minibuffer.el ends here
